﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace PeshoMVC.MyHelpers
{
    public static class QueryableExtensions
    {
        private static MethodInfo StringContainsMethod;
        private static MethodInfo StringStartsWithMethod;

        static QueryableExtensions()
        {
            Type[] singleStringParam = new[] { typeof(string) };
            StringContainsMethod = typeof(string).GetMethod("Contains", singleStringParam);
        }

        public static IQueryable<T> AppendTextFilter<T>(
            this IQueryable<T> queryable,
            Expression<Func<T, string>> memberSelector,
            string condition,
            string value)
        {
            Expression expression = null;
            switch (condition)
            {
                case "select criteria": return queryable;
                case "is not equal to":
                    expression = Expression.NotEqual(
                                    memberSelector.Body,
                                    Expression.Constant(value));
                    break;

                case "is equal to":
                    expression = Expression.Equal(
                                    memberSelector.Body,
                                    Expression.Constant(value));
                    break;

                case "contains":
                    expression = Expression.Call(
                                    memberSelector.Body,
                                    StringContainsMethod,
                                    Expression.Constant(value));
                    break;

                default:
                    throw new NotSupportedException(string.Format("'{0}' is not a supported condition", condition));
            }

            var lambda = Expression.Lambda<Func<T, bool>>(
                            expression,
                            memberSelector.Parameters);
            return queryable.Where(lambda);
        }

        public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
        {
            var param = Expression.Parameter(typeof(T), "p");
            var prop = Expression.Property(param, SortField);
            var exp = Expression.Lambda(prop, param);
            string method = Ascending ? "OrderBy" : "OrderByDescending";
            Type[] types = new Type[] { q.ElementType, exp.Body.Type };
            var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
            return q.Provider.CreateQuery<T>(mce);
        }

        public static IQueryable<T> AppendQuery<T>(this IQueryable<T> queryable, Expression<Func<T, string>> memberSelector, string value)
        {
            if (value == null || value == "default")
            {
                return queryable;
            }

            Expression expression = Expression.Equal(memberSelector.Body, Expression.Constant(value));

            var lambda = Expression.Lambda<Func<T, bool>>(expression, memberSelector.Parameters);
            return queryable.Where(lambda);
        }

        public static IQueryable<T> GreaterThanOrEqual<T>(this IQueryable<T> queryable, DateTime? value)
        {
            var param = Expression.Parameter(typeof(T), "x");
            var prop = Expression.Property(param, "Birthday");
            var exp = Expression.Lambda(prop, param);

            MemberExpression member = Expression.Property(param, "Birthday");

            Expression expression = Expression.GreaterThanOrEqual(member, Expression.Constant(value));

            var lambda = Expression.Lambda<Func<T, bool>>(expression);
            return queryable.Where(lambda);
        }

        public static IQueryable<T> LessThanOrEqual<T>(this IQueryable<T> queryable, DateTime? value)
        {
            var param = Expression.Parameter(typeof(T), "x");
            var prop = Expression.Property(param, "Birthday");
            var exp = Expression.Lambda(prop, param);

            Expression expression = Expression.LessThanOrEqual(exp, Expression.Constant(value));

            var lambda = Expression.Lambda<Func<T, bool>>(expression);
            return queryable.Where(lambda);
        }
    }
}